package cc.owoo.godpen.structure;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Iterator;

/**
 * ID表，针对ID优化的HashMap（线程安全的）
 * Created by nimensei
 * 2022-05-08 上午 11:48
 **/
public class IDMap<T> implements Iterable<T> {
    private final Object lock = new Object();// 线程锁
    private final HashMap<Integer, CustomLinkedList.Node<Block>> blockMap = new HashMap<>();// 索引用的块
    private final CustomLinkedList<Block> blockList = new CustomLinkedList<>();// 遍历用的块
    private final Class<T> itemClass;// 元素类型
    private final int blockSize;// 块大小
    public int size;// 数组长度

    /**
     * 创建分块数组，每个块1000个
     *
     * @param itemClass 元素类型
     */
    public IDMap(Class<T> itemClass) {
        this(itemClass, 1000);
    }

    /**
     * 创建分块数组
     *
     * @param itemClass 元素类型
     * @param block     块大小
     */
    public IDMap(Class<T> itemClass, int block) {
        if (itemClass == null)
            throw new NullPointerException("元素类型不能为空");
        this.blockSize = Math.max(1, block);
        this.itemClass = itemClass;
    }

    /**
     * 设置元素
     *
     * @param id   id值
     * @param item 元素，传null则为删除
     */
    public synchronized void set(int id, T item) {
        getBlock(id / blockSize).value().set(id % blockSize, item);
    }

    /**
     * 获取元素
     *
     * @param id id值
     * @return id对应的元素
     */
    public synchronized T get(int id) {
        var block = blockMap.get(id / blockSize);
        if (block == null)
            return null;
        return block.value().get(id % blockSize);
    }

    /**
     * 获取块
     *
     * @param blockID 块ID值
     * @return 块
     */
    private CustomLinkedList.Node<Block> getBlock(int blockID) {
        synchronized (lock) {
            return blockMap.computeIfAbsent(blockID, integer -> {
                Block block = new Block();
                synchronized (lock) {
                    return block.node = blockList.addLast(block);
                }
            });
        }
    }

    /**
     * 清空数据
     */
    public void clear() {
        synchronized (lock) {
            blockMap.clear();
            blockList.clear();
            size = 0;
        }
    }

    /**
     * 获取元素数量
     *
     * @return 元素数量
     */
    public int size() {
        return size;
    }

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            CustomLinkedList.Node<Block> node;
            int index = 0;
            private T item;

            {
                synchronized (lock) {
                    node = blockList.getFirstNode();
                }
            }

            @Override
            public boolean hasNext() {
                while (node != null) {
                    var block = node.value().array;
                    while (index < block.length) {
                        if ((item = block[index]) != null)
                            return true;
                        ++index;
                    }
                    node = node.next();
                    index = 0;
                }
                return false;
            }

            @Override
            public T next() {
                ++index;
                return item;
            }
        };
    }

    /**
     * 块
     */
    private class Block {
        public T[] array;
        public int size;
        private CustomLinkedList.Node<Block> node;

        public Block() {
            array = (T[]) Array.newInstance(itemClass, blockSize);
        }

        /**
         * 设置元素
         *
         * @param index 元素下标
         * @param item  元素值，如果为null则进行删除
         */
        public synchronized void set(int index, T item) {
            if (item == null) {
                if (array[index] == null)
                    return;
                array[index] = null;
                --IDMap.this.size;
                if (--size == 0) {
                    synchronized (lock) {
                        node.remove();
                    }
                    node = null;
                }
            } else {
                if (array[index] == null) {
                    ++size;
                    ++IDMap.this.size;
                }
                array[index] = item;
            }
        }

        /**
         * 获取元素
         *
         * @param index 元素下标
         * @return 元素对象
         */
        public T get(int index) {
            return array[index];
        }
    }
}
